﻿using System;
using System.Configuration;
using System.IO;
using System.Web;
using System.Xml;
using System.Xml.Xsl;
using Altairis.Nemesis.Gallery.Configuration;

namespace Altairis.Nemesis.Gallery {
    public class GalleryListingModule : IHttpModule {

        GallerySection config;

        public void Dispose() {
            // NOOP
        }

        public void Init(HttpApplication context) {
            // Read configuration
            this.config = GallerySection.GetCurrentOrDefault();

            // Hook-up event
            context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
        }

        void context_PreRequestHandlerExecute(object sender, EventArgs e) {
            HttpApplication app = sender as HttpApplication;

            // Check if it's request for directory listing
            string path = app.Request.PhysicalPath;
            if (!System.IO.Directory.Exists(path)) return;

            // Add ending slash, if needed
            if (!app.Request.Url.ToString().EndsWith("/")) app.Response.Redirect(app.Request.Url.ToString() + "/");

            // Create XML document with list of files and folders
            XmlDocument listing = CreateDirectoryListing(path, "*.jpg", "*.*");

            string templateFileName = app.Server.MapPath(this.config.Templates.DirectoryListing);
            if (System.IO.File.Exists(templateFileName)) {
                // XSL template found -- transform
                XslCompiledTransform tran = new XslCompiledTransform();
                tran.Load(templateFileName);
                tran.Transform(listing, new ConfiguredXsltArgumentList(), app.Context.Response.OutputStream);
                app.Context.Response.ContentType = "text/html";
            }
            else {
                // XSL template not found -- send raw XML
                app.Context.Response.ContentType = "text/xml";
                using (XmlWriter w = new XmlTextWriter(app.Context.Response.OutputStream, System.Text.Encoding.UTF8)) {
                    listing.Save(w);
                }
            }

            // Cancel any handler call
            app.Context.Handler = null;

            // Setup caching 
            app.Context.Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);
            app.Context.Response.Cache.SetExpires(DateTime.Now.AddHours(1));
            app.Context.Response.AddFileDependency(path);
            app.Context.Response.AddFileDependency(templateFileName);
            app.Context.Response.Cache.SetLastModifiedFromFileDependencies();
            app.Context.Response.Cache.SetETagFromFileDependencies();
        }

        static XmlDocument CreateDirectoryListing(string path, string fileMask, string folderMask) {
            // Get parent folder information
            DirectoryInfo parentFolder = new DirectoryInfo(path);

            // Create root element
            XmlDocument doc = new XmlDocument();
            doc.AppendChild(doc.CreateElement("directoryListing"));
            if (!parentFolder.Exists) return doc; // folder not found
            doc.DocumentElement.SetAttribute("title", parentFolder.Name.Replace('_', ' '));

            // Check if this folder is web root
            bool isRoot = parentFolder.FullName.TrimEnd('\\').Equals(System.Web.HttpContext.Current.Request.PhysicalApplicationPath.TrimEnd('\\'), StringComparison.OrdinalIgnoreCase);
            doc.DocumentElement.SetAttribute("isRoot", isRoot.ToString());

            // List subfolders matching folderMask
            foreach (DirectoryInfo subFolder in parentFolder.GetDirectories(folderMask)) {
                // Exclude system folders
                if (subFolder.Name.StartsWith("App_", StringComparison.OrdinalIgnoreCase)) continue;
                if (subFolder.Name.Equals("bin", StringComparison.OrdinalIgnoreCase)) continue;

                // Create <folder> element
                XmlElement folder = doc.CreateElement("folder");
                folder.SetAttribute("title", subFolder.Name.Replace('_', ' '));
                folder.SetAttribute("name", subFolder.Name);
                folder.SetAttribute("creationTime", subFolder.CreationTime.ToString("o"));
                folder.SetAttribute("lastWriteTime", subFolder.LastWriteTime.ToString("o"));

                // Append it to list
                doc.DocumentElement.AppendChild(folder);
            }

            // List files matching fileMask
            foreach (FileInfo subFile in parentFolder.GetFiles(fileMask)) {
                // Create <file> element
                XmlElement file = doc.CreateElement("file");
                file.SetAttribute("title", subFile.Name.Replace('_', ' '));
                file.SetAttribute("name", subFile.Name);
                file.SetAttribute("creationTime", subFile.CreationTime.ToString("o"));
                file.SetAttribute("lastWriteTime", subFile.LastWriteTime.ToString("o"));
                file.SetAttribute("size", subFile.Length.ToString());

                // Append it to list
                doc.DocumentElement.AppendChild(file);
            }

            return doc;
        }

    }
}